﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Ordinaire.Db;
using Ordinaire.Taxonomy;
using Ordinaire.Security.Data;

namespace Ordinaire.Security
{
    /// <summary>
    /// Represents a data manager that handles user roles.
    /// </summary>
    public class RoleDataManager : HierarchyDataManager<OContext>
    {
        #region Variables

        /// <summary>
        /// Instance of Ordinaire.Security.UserDataManager.
        /// </summary>
        private UserDataManager userDataManager;

        #endregion

        #region Constructor

        /// <summary>
        /// Creates new instance of Ordinaire.Security.RoleDataManager.
        /// </summary>
        public RoleDataManager()
        {
            this.userDataManager = new UserDataManager();
        }

        #endregion

        #region Public methods

        /// <summary>
        /// Returns value indicating whether the specified name exists in the data source.
        /// </summary>
        /// <param name="name">the role name</param>
        /// <returns>true if specified name already exists; otherwise false</returns>
        public bool Exists(string name)
        {
            return base.ExistsByName<Roles>(name);
        }

        /// <summary>
        /// Returns all the roles stored in the data source.
        /// </summary>
        /// <returns>a list of data stored in the data source</returns>
        public List<Roles> GetAll()
        {
            var query = from data in Context.Roles
                        select data;
            return query.ToList();
        }

        /// <summary>
        /// Returns the data with the specified rolename.
        /// </summary>
        /// <param name="name">the name of the role to search</param>
        /// <returns>the corresponding data</returns>
        public Roles GetByName(string name)
        {
            return base.GetByName<Roles>(name);
        }

        /// <summary>
        /// Returns list of roles that matches with the specified array of role names.
        /// </summary>
        /// <param name="names">array of role names</param>
        /// <returns>list of matching data</returns>
        public List<Roles> GetByNames(string[] names)
        {
            return base.GetByNames<Roles, int, string>(names);
        }

        /// <summary>
        /// Gets a list of the roles that a specified user is in.
        /// </summary>
        /// <param name="username">the user to return a list of roles for</param>
        /// <returns>a list of role data for the specified user</returns>
        public List<Roles> GetAllForUser(string username)
        {
            Users user = userDataManager.GetByName(username);
            if (user == null)
            {
                throw ExceptionFactory.Create<ArgumentException>(ExceptionMessage.NotExists, String.Format("User name '{0}'", username));
            }

            OContext context = Context;
            var query = from userRoles in context.UserRoles
                        where userRoles.UserId == user.Id
                        select userRoles.Role;
            return query.ToList();
        }

        /// <summary>
        /// Insert new data with the specified role name.
        /// </summary>
        /// <param name="name">new role name</param>
        /// <returns>data created</returns>
        public Roles Insert(string name)
        {
            if (ExistsByName<Roles>(name))
            {
                ExceptionFactory.Create<OException>(ExceptionMessage.AlreadyExists, String.Format("Role name '{0}'", name));
            }

            Roles role = new Roles
            {
                Name      = name
            };

            Roles[] data = Insert<Roles, int, string>(role);
            if (data == null || data.Length == 0)
            {
                return null;
            }
            return data[0];
        }

        /// <summary>
        /// Inserts new data with the specified string array of user names and roles.
        /// </summary>
        /// <param name="usernames">a string array of user names</param>
        /// <param name="roleNames">a string array of role names</param>
        /// <returns>data created</returns>
        public UserRoles[] InsertUserRoles(string[] usernames, string[] roleNames)
        {
            List<Users> userData = userDataManager.GetByNames(usernames);
            List<Roles> roleData = GetByNames(roleNames);

            List<UserRoles> userRoleData = new List<UserRoles>();
            foreach (Users user in userData)
            {
                foreach (Roles role in roleData)
                {
                    UserRoles newData = new UserRoles
                    {
                        UserId = user.Id,
                        RoleId = role.Id
                    };
                    userRoleData.Add(newData);
                }
            }

            return base.Insert<UserRoles, int, string>(userRoleData.ToArray());
        }

        #endregion
    }
}
